<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8">
    <title>Sucha's Blog - Archive for November, 2021</title>
    <meta name="generator" content="MarkdownProjectCompositor.lua">
    <meta name="author" content="Sucha">
    <meta name="keywords" content="suchang, programming, Linux, Lua">
    <meta name="description" content="Sucha's blog">
    <link rel="shortcut icon" href="../images/ico.png">
    <link rel="stylesheet" type="text/css" href="../styles/blog.css">
    <link rel="stylesheet" type="text/css" href="../styles/prism.min.css">
    <style id="site_theme"></style>
  </head>
  <body>
    <div id="body">
      <div id="text">
	   <!-- Page published by cmark-gfm begins here --><h1>Sucha's Blog ~ Archive for November, 2021</h1>
<p><a id="p4"></a></p>
<div class="date">21年11月30日 周二 21:24</div>
<h2>div 内容过长的显示</h2>
<p>js 入门的简单问题，我还是 baidu 了一下，花了一些时间，解决办法需要 CSS，设置 height 或者 max-height，然后设置 overflow 属性，如下</p>
<pre><code class="language-css">.code {
    max-height: 1024px;
    overflow: auto;
}
</code></pre>
<p>实际上，我是通过 js 来设置 docuementId 属性的，因为监听了 resize 事件，我觉得加了下面的函数更完善，可以根据可见区域到校重新 layout，如下：</p>
<pre><code class="language-js">window.addEventListener('resize', function(){
    if (document.body.clientWidth &lt; 1024) {
        document.getElementById('tag').removeAttribute('style');
    } else {
        document.getElementById('tag').setAttribute('style', 'max-height: ' + (document.body.clientHeight) + 'px;overflow:auto;');
    }
})
setTimeout(() =&gt; {
    var event = new Event('resize');
    window.dispatchEvent(event);
}, 1);
</code></pre>
<p>用了 setTimeout 来激活 resize，所以只需要设置一个 resize 监听就好，上面 js 意思是如果可见宽度 &lt; 1024，其实我是将其当成 phone 显示的，就不需要滚动条了。</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2021-11.html#p4">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p3"></a></p>
<div class="date">21年11月25日 周四 22:51</div>
<h2>基于 luarocks/luastatic 的二进制打包工具</h2>
<p><a href="https://github.com/ers35/luastatic">luastatic</a> 可以打无依赖的二进制包，但是官方的版本有一些限制，比如在命令行里面，lua 源文件之只能是基于当前打包目录的相对路径。</p>
<p>而实际上，由于 lua 的 LUA_PATH、LUA_CPATH 以及 package.path 和 package.cpath 太自由，可以定制很多种搜索优先级，以及目录相对关系，打包目录下的相对路径，就不能反映源码上面的 require 路径逻辑了。</p>
<p>于是我基于上面的 luastatic，做了一点改进，可以输入相对路径，代码在 <a href="https://github.com/lalawue/luastatic">lalawue/luastatic</a>。</p>
<p>举个例子，一方面，我重度使用 luarocks 安装的库，比如 cincau，默认目录可以是 /usr/local/share/lua/5.1/cincau，而另外自己写的源码，都在 app 目录下，然后设置了如下的 LUA_PATH：</p>
<pre><code class="language-shell">$ export LUA_PATH=&quot;/usr/local/share/lua/5.1/cincau/?.lua;./app/?.lua&quot;
</code></pre>
<p>这样在 require 的时候，省却了很多相对路径。如果使用官方的 luastatic 打包，就得建立相对目录，然后作为命令行参数输入给 luastatic，用我修改后的 luastatic，只需要先设置 shell 变量：</p>
<pre><code class="language-shell">$ export LUASTATIC_SOURCE_PATH=&quot;/usr/local/share/lua/5.1/cincau/;app/;&quot;
</code></pre>
<p>注意目录最后的 '/' 和 ';'，如果输入的 lua 源文件检测到相应的路径前缀，会将这个前缀去掉，这样就实现了 LUA_PATH 和 package.path 的功能，运行时的路径搜索和打包时候的路径一致了，不需要构建相对目录。</p>
<p>另外需要注意的是，虽然 luastatic 支持生成无依赖的 binary，但那只是对应官方的 lua，luajit 依赖的 .so 库还是需要动态加载的，比如很多的 ffi.load 依赖的是系统的 dlopen。这个我看了一些文章，大佬们根据安全原则，系统的接口都不支持从内存中加载一个动态库，虽然某些平台上通过 mmap 是可以做到的，但是这个总感觉不大好。</p>
<p>那如果是基于 luajit，lua 源文件是打到 binary 里面了，但是依赖的 so 库，是在运行前，通过 export LD_LIBRARY_PATH 或者 DYLD_LIBRARY_PATH 来设置 so 搜索路径，做成外部库加载的。</p>
<p>通过这样的方式做成的 binary 发布包，可以在对应平台上不依赖 luarocks 就可以运行，安全性也好一些，发布更新都很方便。</p>
<hr>
<p>话说昨天看了一下 <a href="https://programming-language-benchmarks.vercel.app/">Programming Language and compiler Benchmarks</a> 里面 go vs java，go vs javascript 以及 go vs luajit，不得不说 go 性能确实好，速度跟 java 一样，但是内存消耗少了不知多少。</p>
<p>luajit 平均速度是 go 的一半，内存消耗多了一些，javascript 速度跟 luajit 类似，但内存消耗多很多。</p>
<p>如果是从头学起的话，我觉得 go 是要比 java 简单很多的，工程属性方面，go 更是标杆，如果算上多线程 goroutine 的话，毫无疑问，go 是首选。多线程多难搞呀，所以小公司很可能都慢慢转向 go 了，反正我是觉得真的香呀，现在都有点后悔搞 luajit 了，因为速度是赶不上 go 的，基础工具又差这么多。</p>
<p>上面的所谓打二进制包，就是补充了一部分基础工具的缺失，即便不说标杆 go，人家 deno 也是可以打成二进制包的。</p>
<p>luajit 不多的优点，一方面是不需要编译，虽然 go 编译很快，但是不需要编译连这个部分都省略了；另外就是 luajit 语法几乎是固定了，不大担心后续会有比较大的变动，唯一担心的额是 Mike Pall 是 luajit 几乎唯一的维护者，社区太小了，这个软件太高深，往后一些平台的支持以及升级估计够呛，更新也慢。</p>
<p>lua 语言的的优点在 web 开发上面不大体现得出来，相比之下，比如虚拟机很小，这样轻量的虚拟环境、沙箱，可以造就诸如国内有名的 <a href="https://github.com/cloudwu/skynet/">skynet</a>。</p>
<p>lua 语法更简单，类型都可以少关心，但并不意味说这个语言抽象能力不够，或者扩展不行，如果 metatable 抽象得好，可能性太多，OO 小菜一碟，比如我之前基于 lua 构建的 mooncake；所以，也没有泛型渴望症；但是因为缺乏类型系统，那么测试系统得自己保证才放心。话说，也是因为语言自由，lua 很多库的源码，相对 go 源码来说，可太难懂了。</p>
<p>最近还看了 WASM，感觉也很香，go 自从 1.11 后，支持输出 WASM，但 WASM 这个技术还比较早期，W3C 上面到了提案 2 的阶段，还没有多线程这样的标准。从上面效率对比，跟 go 是一个级别。</p>
<p>这次 post 先到这里吧。</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2021-11.html#p3">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p2"></a></p>
<div class="date">21年11月17日 周三 21:52</div>
<h2>诛仙读后感</h2>
<p>以前没读过仙侠系列，算是第一次读。</p>
<p>前段意境真的很美，特别是上了青云山拜师之后，掌管大厨的这一段，跟师姐砍竹子，带领一猴一狗每天在这仙山里面慢慢悠悠升级，就觉得这感觉很好。</p>
<p>而且内线是有内功见长，又少不了武侠里面的捡到了绝世大宝贝，比如土到不能更土的烧火棍，以及后期那些救人于水火，法力无边的宝贝，更是让人羡慕神往。</p>
<p>起伏线的大起大落也很经典，以及一直不受师傅待见，但最后唯一一个给师傅长脸的徒弟，又比如被掌门下杀心的一段而成为鬼厉，从此杀人无数。</p>
<p>支线里面厉害的有万剑一，以及仙人指路，野狗跟仙人指路能走到一块，这个组合在人心里面又重了，这条线里面的仙人指路水平太高，应该是作者的用来补充剧情的旁白。</p>
<p>但是也有遗憾的支线，比较不能理解的是陆雪琪，一直走不到明线，但是暗线又这么强烈，难受。</p>
<p>前面写得太好了，觉得跟兽人大战之后，跟魔教对决的这一段，马马虎虎，没啥感觉。</p>
<p>总体可以打 7.5 ~ 8 分了。</p>
<div class="category"><a href="CategoryReading.html">CategoryReading</a> / <a href="2021-11.html#p2">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p1"></a></p>
<div class="date">21年11月11日 周四 00:33</div>
<h2>Mithril 使用心得（1）</h2>
<p>这个网站已经诞生 10 多年，但其是静态页面，基于 XHTML 结构，而不是最新的 HTML5，站长本人是专业 APP 开发，
H5/Web 开发比较业余，对 <a href="https://mithril.js.org/">Mithril</a> 的使用感觉还不够深入，但也算趟了一些坑，
可以作为心得说一下的。</p>
<p>说一下为何不使用 React、Vue 或者 Preact，以及 Angular 等等其他框架，没什么原因，因为其实入手之前看了非常多的入门对比文章，
在对比的过程中总是要上手的嘛，因为看到 Mithril 吹自己简单，其网站入门教程的介绍也是非常到位的（React 没能一下学会，
但是 Mithril 可以），加上不需要 nodejs 这样的依赖，不需要 build，学习的曲线感觉很平缓。</p>
<p>话说我之前是一个连 SPA（Single Page Application）都不懂的人，通过 Mithril 的教程，学会了基础的 component，
route、以及 XHR，基本上一个单页面就可以搞定了。</p>
<p>再说还有 <a href="https://stackblitz.com/">stackblitz</a> 这样的平台，入门是很方便的。因为是个无需 build 的方案，
简单的 HTML5 + rel 标签带入 Mithril 就可以开始工作了。</p>
<p>js 我也只懂皮毛，很多东西都得百度，没关系，出来的程序能用就行。最后做了 2 个 SPA 页面，一个是 wiki，一个是 note 便签。</p>
<p>wiki 的相对简单一些，大概 2 - 3 天才搞定，学习了 route，但是内部的很多状态我是通过一个全局状态变量来控制的。
便签这个页面没有用到路由，纯内部状态跳转刷新，两个都没有用到 browser 的 history，浏览器的返回按钮会退回上个 URL。</p>
<p>Mithirl 的 bind 或者 route，都可以仅指定页面的一个部分，一个 element 来刷新，内部是根据 vnode 的变化来判断是否更新的，
这个 vnode 变化感觉有点悬乎，比如我在创建一些子节点的时候，attribute 是有变化的，但是 Mithril 感觉不到这个变化，
所以我不得不在外层做更明显的 vnode 变化，比如增加一个用不到的 span 标签这样，来确保重新输出 HTML 节点。</p>
<p>Mithril 限定 60hz 刷新确实也不错，但是遇到一些极端情况，比如自己本地测试的时候，网络返回就挺快的，当我在用内部变量控制
vnode 输出的不同时，因为间隔过短，虽然变量有切换状态，但 Mithril 间隔读取到的其实都是同一个状态下的 vnode，
而忽略了中间这个变量状态下 vnode 的输出，最后导致认为没有变化，不需要刷新，出了问题，如下：</p>
<pre><code class="language-source">// 如下的 A、B 表示变量值，后面数字表示 vnode 结构为 A 但内容不同，这里需要保证 A1 -&gt; B 间隔 1/60 秒
A1 -&gt; B -&gt; A2
</code></pre>
<p>所以有时候，需要将一些操作延时到 60hz 单次刷新之外才行，让 Mithril 检测到 vnode 变化。</p>
<p>Mithril 教程用的是古老的创建 vnode 的函数调用方案，但其实可以配合 preact 的 <a href="https://github.com/developit/htm">htm</a>，
我觉得使用方式上要比教程里面的简单很多，比如教程是下面这样创建 vnode 的：</p>
<pre><code class="language-js">// 下面 title_str 是变量
// &lt;h1 class=&quot;title&quot;&gt;My first app&lt;/h1&gt;
const title_str = 'title';
m(&quot;h1&quot;, {class: title_str}, &quot;My first app&quot;)
</code></pre>
<p>如果用了 <a href="https://github.com/developit/htm">htm</a>，可以像下面这样：</p>
<pre><code class="language-js">// 先绑定，最后也是通过 m 函数来输出 vnode 的
const html = htm.bind(m);
const title_str = 'title';
html`&lt;h1 class=&quot;${title_str}&quot;&gt;My first app&lt;/h1&gt;`
</code></pre>
<p>明显是使用了 htm 的代码更容易定位问题。</p>
<p>因为追求短平快，单个 js 文件就完成了 wiki 或者 note 便签这样的功能，所以 Mithril 结构化方面我是没有发言权了，教程上面
呼吁结构化组件，分解功能模块我是一点都没用上。</p>
<p>先这样吧。</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2021-11.html#p1">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p0"></a></p>
<div class="date">21年11月07日 周日 11:46</div>
<h2>stunnel 的 SNI 配置</h2>
<p>因为是很小的网站，资源也有限，感觉没必要用上 openresty 这个大件。</p>
<p>先贴配置：</p>
<pre><code class="language-ini">debug=info
output=/path/to/access.log
pid=/path/to/process.pid
client=no

[https]
accept = 443
connect = 127.0.0.1:port1
cert = /path/to/default.cer
key = /path/to/default.key

[site1]
connect = 127.0.0.1:port1
sni = https:site1.domain
cert = /path/to/site1.cer
key = /path/to/site1.key

[site2]
connect = 127.0.0.1:port2
sni = https:*.site2.domain
cert = /path/to/site2.cer
key = /path/to/site2.key
</code></pre>
<p>说一下上面的配置。</p>
<p>这是类似 INI 方式的配置文件，首先定义是 server 端的配置，设置 access.log 和 process.pid 文件的位置。</p>
<p>之后设置默认协议，比如叫做 https，监听 443 端口，并设置连接地址，使用默认的 cert 和 key 做验证，这里其实是可以跟下面的 site1 或者 site2 用同样的 cert 和 key 配置。</p>
<p>之后分别设置不同网站对应的处理地址，比如 site1，连接的是 port1，注意 sni 字段配置，第一个是协议，就是上面描述过的 https，而不是 HTTPS，之后是需要处理的 domain，这里只处理二级域名，没有通配字，最后加入网站申请到的 cert 和 key。</p>
<p>site2 的 sni 用了通配字，所有 site2.domain 的二级、三级域名都可通过验证处理。</p>
<div class="category"><a href="CategoryLinux.html">CategoryLinux</a> / <a href="2021-11.html#p0">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<!-- Page published by cmark-gfm ends here -->
  <div id="foot">2004-<script>var d = new
	Date();document.write(d.getFullYear())</script> &copy;
	Sucha. Powered by MarkdownProjectCompositor.
  </div>
  </div><!-- text -->
  <div id="sidebar">
  </div><!-- sidebar -->
  <script src="../js/prism.min.js" async="async"></script>
  <script src="../js/blog_sidebar.js"></script>
  </div> <!-- body -->
</body>
</html>